﻿namespace System.Xml.Serialization.Internal
{
    using System.Collections.Generic;
    using System.Reflection;

    internal static class XmlRuntimePropertyHelper
    {
        private static IXmlRuntimeProperty CreateProperty(object value, Type type, string propertyName)
        {
            PropertyInfo property = type.GetProperty(propertyName);
            if (property != null)
            {
                return new XmlRuntimeProperty(value, type, property);
            }
            return null;
        }

        private static XmlRuntimeProperty CreatePropertyInternal(object value, string propertyName)
        {
            Type type = value.GetType();
            PropertyInfo property = type.GetProperty(propertyName);
            if (property != null)
            {
                return new XmlRuntimeProperty(value, type, property);
            }
            return null;
        }

        public static T[] GetInterfaceAttributes<T>(Type interfaceType)
            where T : Attribute
        {
            List<T> attributes = new List<T>();
            attributes.AddRange((T[])interfaceType.GetCustomAttributes(typeof(T), true));
            foreach (Type type in interfaceType.GetInterfaces())
            {
                attributes.AddRange((T[])type.GetCustomAttributes(typeof(T), true));
            }
            return attributes.ToArray();
        }

        public static IEnumerable<PropertyInfo> GetInterfaceProperties(Type interfaceType)
        {
            foreach (Type type in interfaceType.GetInterfaces())
            {
                foreach (PropertyInfo property in type.GetProperties())
                {
                    yield return property;
                }
            }
        }

        public static IEnumerable<PropertyInfo> GetInterfacePropertiesWithType(Type interfaceType)
        {
            if (interfaceType != null)
            {
                foreach (Type type in interfaceType.GetInterfaces())
                {
                    foreach (PropertyInfo property in type.GetProperties())
                    {
                        yield return property;
                    }
                }
                foreach (PropertyInfo property in interfaceType.GetProperties())
                {
                    yield return property;
                }
            }
        }

        public static IEnumerable<IXmlRuntimeProperty> CreateProperties(object value)
        {
            foreach (PropertyInfo property in GetInterfaceProperties(value.GetType()))
            {
                IXmlRuntimeProperty runtimeProperty = CreateProperty(value, property.Name);
                if (runtimeProperty != null)
                {
                    yield return runtimeProperty;
                }
            }
        }

        public static IXmlRuntimeProperty CreateProperty(object value, string propertyName)
        {
            return CreatePropertyInternal(value, propertyName);
        }

        public static IXmlRuntimeProperty<T> CreateProperty<T>(object value, string propertyName)
        {
            IXmlRuntimeProperty property = CreateProperty(value, propertyName);
            if (property != null)
            {
                IXmlRuntimeProperty<T> runtimeProperty = property.GetRuntimeProperty<T>();
                if (runtimeProperty != null)
                {
                    return runtimeProperty;
                }
            }
            return null;
        }

        public static PropertyInfo GetInterfaceProperty(Type interfaceType, string propertyName)
        {
            PropertyInfo property = null;
            foreach (Type type in interfaceType.GetInterfaces())
            {
                property = type.GetProperty(propertyName);
                if (property != null)
                {
                    return property;
                }
            }
            property = interfaceType.GetProperty(propertyName);
            if (property != null)
            {
                return property;
            }
            return null;
        }
    }
}
